#include <xc.h>
#include <stdint.h>

// --- НАСТРОЙКИ МИКРОКОНТРОЛЛЕРА (Конфигурация) ---
#pragma config FOSC = HS2       // Кварцевый резонатор HS (High Speed)
#pragma config PLLCFG = ON      // Включить PLL x4 (умножитель частоты)
#pragma config PWRTEN = ON      // Таймер задержки при подаче питания
#pragma config BOREN = OFF      // Сброс при просадке питания отключен
#pragma config WDTEN = OFF      // Сторожевой таймер отключен
#pragma config MCLRE = ON       // Вывод MCLR (Reset) включен
#pragma config SOSCSEL = DIG    // Вспомогательный генератор в режиме Digital
#pragma config XINST = OFF      // Расширенный набор инструкций выключен

// Определяем рабочую частоту для функций задержки __delay_us и __delay_ms
// Внешний кварц 4МГц * 4 (PLL) = 16 000 000 Гц
#define _XTAL_FREQ 16000000 

// Назначение выводов (согласно вашей схеме)
#define K_TX LATBbits.LATB0     // Передача в K-Line (RB0). Инвертируется транзистором!
#define K_RX PORTCbits.RC1      // Прием из K-Line (RC1). Прямой сигнал.

// Расчет задержки для 10400 бод: 1/10400 = 96.15 мкс.
// Учитывая время на выполнение инструкций в цикле, 92 мкс — идеальное значение.
#define BIT_DELAY 92            

// Переменная для фильтрации эха: запоминаем, что мы сами отправили
volatile uint8_t last_tx_byte = 0;

// --- ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ---

// Длинная задержка в миллисекундах (используется для Fast Init)
void delay_ms_long(uint16_t ms) {
    while(ms--) __delay_ms(1);
}

// Инициализация аппаратного UART1 для связи с компьютером
void terminal_init_10400() {
    TRISCbits.TRISC6 = 0;   // TX на выход
    TRISCbits.TRISC7 = 1;   // RX на вход
    
    // Настройка скорости 10400 бод при 16МГц (Fosc / (4 * (SPBRG + 1)))
    BAUDCON1bits.BRG16 = 1; // 16-битный делитель
    TXSTA1bits.BRGH = 1;    // Режим высокого разрешения
    SPBRGH1 = (383 >> 8);   // Старший байт (0x01)
    SPBRG1 = (383 & 0xFF);  // Младший байт (0x7F)
    
    TXSTA1bits.SYNC = 0;    // Асинхронный режим
    RCSTA1bits.SPEN = 1;    // Включить последовательный порт
    TXSTA1bits.TXEN = 1;    // Разрешить передачу
    RCSTA1bits.CREN = 1;    // Разрешить непрерывный прием
}

// Отправка байта в компьютер через аппаратный UART1
void terminal_write(uint8_t data) {
    while(!PIR1bits.TX1IF); // Ждем, пока освободится буфер передачи
    TXREG1 = data;
}

// --- ПРОГРАММНЫЙ UART (ДЛЯ K-LINE) ---

// Передача байта в К-линию (с учетом инверсии на транзисторе)
void soft_uart_write(uint8_t data) {
    // Start Bit: лог. 1 на выходе МК -> транзистор открыт -> 0V на К-линии
    K_TX = 1; __delay_us(BIT_DELAY); 
    
    // Передаем 8 бит данных (начиная с младшего)
    for (uint8_t i = 0; i < 8; i++) {
        if (data & 0x01) {
            K_TX = 0; // Лог. 0 на МК -> транзистор закрыт -> +12V на К-линии
        } else {
            K_TX = 1; // Лог. 1 на МК -> транзистор открыт -> 0V на К-линии
        }
        data >>= 1; 
        __delay_us(BIT_DELAY);
    }
    
    // Stop Bit: лог. 0 на выходе МК -> на К-линии +12V (покой)
    K_TX = 0; __delay_us(BIT_DELAY); 
}

// Чтение байта из К-линии (неблокирующее)
uint8_t soft_uart_read_noblock() {
    uint8_t data = 0;
    // Если на входе лог. 1 (+12V), значит данных сейчас нет
    if (K_RX == 1) return 0xFF; 
    
    // Ждем 1.5 длительности бита, чтобы попасть в середину первого бита данных
    __delay_us(BIT_DELAY + (BIT_DELAY / 2));
    
    // Собираем 8 бит данных
    for (uint8_t i = 0; i < 8; i++) {
        if (K_RX) data |= (1 << i);
        __delay_us(BIT_DELAY);
    }
    return data;
}

// --- ГЛАВНАЯ ПРОГРАММА ---

void main(void) {
    // Настройка цифровых портов
    ANCON0 = 0; ANCON1 = 0; // Отключаем АЦП, переводим все пины в цифровой режим
    TRISBbits.TRISB0 = 0;   // RB0 на выход (K-Line TX)
    TRISCbits.TRISC1 = 1;   // RC1 на вход (K-Line RX)
    
    K_TX = 0; // Начальное состояние: транзистор закрыт, на К-линии +12В

    terminal_init_10400();  // Запуск UART для ПК
    delay_ms_long(200);     // Пауза для стабилизации питания

    while(1) {
        // Проверка и сброс ошибки переполнения UART1 (если данные от ПК пришли слишком быстро)
        if (RCSTA1bits.OERR) {
            RCSTA1bits.CREN = 0;
            RCSTA1bits.CREN = 1;
        }

        // --- ЛОГИКА 1: ПЕРЕДАЧА (ПК -> К-Линия) ---
        if (PIR1bits.RC1IF) { // Если пришел байт от OpenDiag
            uint8_t pc_byte = RCREG1;
            
            if (pc_byte == '!') { 
                // Спец-команда для Fast Init (Быстрая инициализация)
                K_TX = 1; delay_ms_long(25); // 0V на 25мс
                K_TX = 0; delay_ms_long(25); // +12V на 25мс
            } else {
                last_tx_byte = pc_byte; // Запоминаем байт
                soft_uart_write(pc_byte); // Шлем в машину
                
                // ВАЖНО: OpenDiag ждет эхо. Мы возвращаем его виртуально,
                // чтобы не зависеть от задержек физического эха в транзисторах.
                terminal_write(pc_byte); 
            }
        }

        // --- ЛОГИКА 2: ПРИЕМ (К-Линия -> ПК) ---
        if (K_RX == 0) { // Если на линии появился старт-бит (0V)
            uint8_t k_byte = soft_uart_read_noblock();
            
            if (k_byte != 0xFF) {
                // Если прочитанный байт НЕ совпадает с тем, что мы только что отправили,
                // значит это реальный ответ от блока управления (ЭБУ).
                if (k_byte != last_tx_byte) {
                    terminal_write(k_byte);
                }
                // Сбрасываем метку последнего байта после обработки
                last_tx_byte = 0xFF; 
            }
        }
    }
}
